#! /usr/bin/python
import glob, sys, os
import DebugMsgDef

DEFAULT_TIME_GAP = 1000*600 # 10 min.

##############################################
# Read csv files.                            #
# Return: node list, a dict of message list. #
##############################################
def readFile(fn):
  nodeID = int(fn[fn.find('node') + 4:-4])
  msgList = []
  print 'Reading', fn
  dfile = open(fn)
  aline = dfile.readline()
  if '0x' in aline:
    print 'Found uncovered csv files. converting...'
    dfile.close()
    import dec2hex
    dec2hex.type2String(fn)
    dfile = open(fn)
  else:
    dfile.seek(0)
  for aline in open(fn):
    msg = SerialMessage(aline)
    msgList.append(msg)
  print len(msgList), 'message found.'
  return nodeID, msgList

def categorizeMessage(msgList):
  tp = DebugMsgDef.debugMsgTypes.values()
  ep = [[] for i in range(len(DebugMsgDef.debugMsgTypes))]
  msgCata = dict(zip(tp,ep))
  for i in xrange(len(msgList)):
    msgCata[msgList[i].type].append(i)
  return msgCata

"""
Generate a csv file, list useful types of debug messages
and their occurances.
"""
def writeSummary(msgType, fn):
  types = DebugMsgDef.TxMessageTypes
  types.extend(DebugMsgDef.TreeMessageTypes)
  types.extend(DebugMsgDef.PdMessageTypes)
  sumStr = 'NodeID,' + ','.join(types) + '\n'
  nodes = msgType.keys()
  nodes.sort()
  for nodeID in nodes:
    msgDict = msgType[nodeID]
    sumStr += str(nodeID) + ','
    sumStr += ','.join([str(len(msgDict[tp])) for tp in types]) + '\n'
  sumFile = open(fn, 'w')
  sumFile.write(sumStr)
  sumFile.close()

def findTxMessage(msgList,typeDict,seq,origin,types,startTime,endTime):
  foundMsg = []
  # find the first msg if seq = -1
  if seq == -1:
    for idx in xrange(len(msgList)):
      msg = msgList[idx]
      if (msg.getOrigin() == origin) and (msg.type in types):
        seq = msg.getSeq() # append will happen in next block
        startTime = msg.serverTime
        endTime = startTime + DEFAULT_TIME_GAP
        break

  if seq != -1:
    for tp in types:
      for idx in typeDict[tp]:
        msg = msgList[idx]
        if msg.serverTime < startTime:
          continue
        elif msg.serverTime > endTime:
          break
        else:
          if (msg.getOrigin() == origin) and (msg.getSeq() == seq):
            foundMsg.append(idx)
            endTime = msg.serverTime + DEFAULT_TIME_GAP
  foundMsg.sort()
  return foundMsg

def findTxSendMessage(msgList,typeDict,seq,origin,time):
  if seq != -1:
    startTime = time - int(DEFAULT_TIME_GAP / 2)
    endTime = time + DEFAULT_TIME_GAP
  else:
    startTime = 0
    endTime = 0
  return findTxMessage(msgList,typeDict,seq,origin,DebugMsgDef.TxSendMessages,startTime,endTime)

def findTxRcvMessage(msgList,typeDict,seq,origin,time):
  if seq != -1:
    startTime = time - int(DEFAULT_TIME_GAP / 2)
    endTime = time + DEFAULT_TIME_GAP
  else:
    startTime = 0
    endTime = 0
  return findTxMessage(msgList,typeDict,seq,origin,DebugMsgDef.TxRcvMessages,startTime,endTime)

"""
find every packet's path
"""
def packetPath(msgDict, msgTypeDict, rootID):
  pktDict = {}
  for nodeID in msgDict.keys():
    if nodeID == rootID:
      continue
    # firstly set packet tuple
    seq = -1
    origin = nodeID
    time = 0
    currHop = nodeID
    nextHop = -1
    pktList = []

    print 'Tracing packets in node', nodeID
    foundList = findTxSendMessage(msgDict[currHop], msgTypeDict[currHop], seq, origin, time)
    while len(foundList) > 0:
      lastFound = msgDict[currHop][foundList[-1]]
      seq = lastFound.getSeq()
      time = lastFound.serverTime
      nextHop = lastFound.getDest()
      txList = []
      hopList = []
      pathStatus = 'LOST'
      txList.append(len(foundList))
      hopList.append(currHop)
      lastHop = currHop

      #print 'pkt:',seq,origin,time
      #print currHop,lastFound.type,

      # now start to look at the next hop. Will stop at no more send.
      rcvFound = findTxRcvMessage(msgDict[nextHop], msgTypeDict[nextHop], seq, origin, time)
      if nextHop == rootID:
        txFound = []
      else:
        txFound = findTxSendMessage(msgDict[nextHop], msgTypeDict[nextHop], seq, origin, time)
      # print 'looking at', nextHop, 'rcv',len(rcvFound), 'tx', len(txFound)
      # print '->',nextHop,

      while nextHop != rootID and (len(txFound) > 0):
        # print possible errors
        if (len(rcvFound)==0) and (len(txFound)>0):
          print 'Send without Rcv @ node',nextHop, 'idx', txFound[-1]
        else:
          rcv = msgDict[nextHop][rcvFound[0]]
          if ORIGINAL == False and (rcv.getSrc() != lastHop):
            print 'Rcv pkt from non-sender @node ',nextHop, 'idx', rcvFound[0]

        txList.append(len(txFound))
        hopList.append(nextHop)
        lastFwd = msgDict[nextHop][txFound[-1]]

        # loop detection
        if lastFwd.getDest() in hopList:
          print 'loop detected', nextHop, '<->', lastFwd.getDest()
          pathStatus = 'LOOP'
          break

        lastHop = nextHop
        nextHop = lastFwd.getDest()

        rcvFound = findTxRcvMessage(msgDict[nextHop], msgTypeDict[nextHop], seq, origin, time)
        if nextHop == rootID:
          txFound = []
        else:
          txFound = findTxSendMessage(msgDict[nextHop], msgTypeDict[nextHop], seq, origin, time)
        # print '->',nextHop,

      # check if the packet made to root node
      if (nextHop == rootID) and len(rcvFound) > 0:
        hopList.append(rootID)
        pathStatus = 'DELIVERED'

      # Finish tracing one packet. record: pkt tuple, tx list, hop list
      # record packet list. value: tuple of (pkt tuple, txList, hopList)
      pktList.append( ((seq,origin,time), hopList, txList, pathStatus) )
      # now looking for the next packet.
      seq = (seq + 1) % 65535
      foundList = findTxSendMessage(msgDict[currHop], msgTypeDict[currHop], seq, origin, time)

    # all Tx packets in currHop are taken care of. record everything.
    pktDict[nodeID]=pktList
    print 'Done.', len(pktList), 'packet traced.'
  return pktDict

def writePath(pathDict, surfix):
  # clean the directory
  for fn in glob.glob(surfix + '*'):
    os.remove(fn)

  # summary: node,# pkt, avgr. PL, delivered, lost, loop
  sumDict={}
  sumpkt = 0; sumpl=0.0; sumtx=0;sumdlv=0; sumlost=0; sumloop=0
  for nodeID in pathDict.keys():
    pathList = pathDict[nodeID]
    sumDict[nodeID] = [nodeID,len(pathList)]
    pl=0.0; tx=0; dlv=0; lost=0; loop=0

    pathFile = open(surfix + str(nodeID), 'w')
    for (pktTuple, hopList, txList, status) in pathList:
      pl += len(hopList)
      tx += sum(txList)
      if status == 'DELIVERED':
        dlv += 1
      elif status == 'LOST':
        lost += 1
      elif status == 'LOOP':
        loop += 1
      else:
        print 'Invaild path status!'

      pathStr = ';'.join([ status, \
                          ','.join(str(i) for i in pktTuple), \
                          ','.join(str(i) for i in hopList),  \
                          ','.join(str(i) for i in txList), \
                          `len(hopList)`, `sum(txList)`, \
                          `max(txList)`, `min(txList)`])
      pathFile.write(pathStr+'\n')
    pathFile.close()

    sumpkt+=len(pathList);sumpl+=pl; sumtx+=tx; sumdlv+=dlv; sumlost+=lost; sumloop+=loop
    if (len(pathList) > 0):
      sumDict[nodeID].extend([pl / float(len(pathList)), tx, dlv,lost,loop])
    else:
      sumDict[nodeID].extend([ 0, tx, dlv,lost,loop])
  # also a summary file
  nodes = sumDict.keys()
  nodes.sort()
  if (sumpkt > 0):
    sumStr = ','.join(str(i) for i in [-1,sumpkt, sumpl/sumpkt, sumtx, sumdlv, sumlost, sumloop])
  else:
    sumStr = ','.join(str(i) for i in [-1,sumpkt, 0, sumtx, sumdlv, sumlost, sumloop])

  for nodeID in nodes:
    sumStr += '\n' + ','.join([str(i) for i in sumDict[nodeID]])
  pathFile = open(surfix + '_summary','w')
  pathFile.write(sumStr)
  pathFile.close()


"""
Main method
"""
def main():
  datadir = sys.argv[1]
  if datadir[-1] != '/':
    datadir = datadir + '/'

  csvlist = glob.glob(datadir + 'node*.csv')
  if len(csvlist) == 0:
    print "No csv files in the directory:", sys.argv[1]
    sys.exit(0)

  rootID = 0

  nodeList = [] # list of nodes
  msgDict = {} # key: node ID, value: list of msg
  msgTypeDict = {} # key: node ID. value: dict(msg type, msg idx)
  for csvfile in csvlist:
    nodeID, msgList = readFile(csvfile)
    if len(msgList) == 0:
      print 'Empyt file:', csvfile
      continue
    nodeList.append(nodeID)
    msgDict[nodeID]=msgList
    print 'Categorizing messages... ',
    msgTypeDict[nodeID] = categorizeMessage(msgList)
    print 'done'
  print len(nodeList), 'files read.'

  # start compute path. need:
  # 1) summary of debug msg: send, pd, and snoop(ones has better ETX)
  # 2) packet path, TX spend on each nodes, total TX, and TX of TP.
  # 3) save the summary and packet path to MATLAB readable format.

  writeSummary(msgTypeDict, datadir + 'summary')
  pathDict = packetPath(msgDict, msgTypeDict, rootID)
  writePath(pathDict, datadir + 'path')


######## ENTRTY #######
if(len(sys.argv) != 2):
  print 'parserMain <data dir>'
  sys.exit(0)

# determine what kind of messages based on dir name :P
if 'orig' in sys.argv[1]:
  from DebugMsgClass_Original import SerialMessage
  ORIGINAL = True
else:
  from DebugMsgClass_Modified import SerialMessage
  ORIGINAL = False

if __name__ == '__main__': main()

